/* $Id$ */
/*
** Copyright (C) 1998-2002 Martin Roesch <roesch@sourcefire.com>
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License Version 2 as
** published by the Free Software Foundation.  You may not use, modify or
** distribute this program under any other version of the GNU General
** Public License.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

/*
** This file contains various routines which we shamelessly steal from other
** opensource products :-P (I love code reuseability idea) 
** 			fygrave@tigerteam.net
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifndef HAVE_SNPRINTF
    #include "snprintf.h"

/* snprintf() and all supporting routines were taken from sendmail, hence the 
 * copyleft message 
 */

/*
 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
 * Copyright (c) 1997 Eric P. Allman.  All rights reserved.
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * By using this file, you agree to the terms and conditions set
 * forth in the LICENSE file which can be found at the top level of
 * the sendmail distribution.
 *
 */

/*
**  SNPRINTF, VSNPRINT -- counted versions of printf
**
**	These versions have been grabbed off the net.  They have been
**	cleaned up to compile properly and support for .precision and
**	%lx has been added.
*/

/**************************************************************
 * Original:
 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
 * A bombproof version of doprnt (sm_dopr) included.
 * Sigh.  This sort of thing is always nasty do deal with.  Note that
 * the version here does not include floating point...
 *
 * snprintf() is used instead of sprintf() as it does limit checks
 * for string length.  This covers a nasty loophole.
 *
 * The other functions are there to prevent NULL pointers from
 * causing nast effects.
 **************************************************************/

void    sm_dopr();
char    *DoprEnd;
int SnprfOverflow;

    #ifndef HAVE_SNPRINTF

/* VARARGS3 */
int
        #ifdef __STDC__
snprintf(char *str, size_t count, const char *fmt, ...)
        #else
snprintf(str, count, fmt, va_alist)
char *str;
size_t count;
const char *fmt;
va_dcl
        #endif
{
    int len;
    VA_LOCAL_DECL

    VA_START(fmt);
    len = vsnprintf(str, count, fmt, ap);
    VA_END;
    return len;
}


        #ifndef luna2
#ifndef HAVE_VSNPRINTF
                
int
vsnprintf(str, count, fmt, args)
char *str;
size_t count;
const char *fmt;
va_list args;
{
    str[0] = 0;
    DoprEnd = str + count - 1;
    SnprfOverflow = 0;
    sm_dopr( str, fmt, args );
    if(count > 0)
        DoprEnd[0] = 0;
    if(SnprfOverflow && tTd(57, 2))
        printf("\nvsnprintf overflow, len = %ld, str = %s",
               (long) count, shortenstring(str, MAXSHORTSTR));
    return strlen((const char *)str);
}

#endif /* !HAVE_VSNPRINTF */
        
        #endif /* !luna2 */
    #endif /* !HASSNPRINTF */

/*
 * sm_dopr(): poor man's version of doprintf
 */

void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth));
void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad));
void dostr __P(( char * , int ));
char *output;
void dopr_outch __P(( int c ));
int SyslogErrno;

void
sm_dopr( buffer, format, args )
char *buffer;
const char *format;
va_list args;
{
    int ch;
    long value;
    int longflag  = 0;
    int pointflag = 0;
    int maxwidth  = 0;
    char *strvalue;
    int ljust;
    int len;
    int zpad;
#if !HAVE_STRERROR && !defined(ERRLIST_PREDEFINED)
    extern char *sys_errlist[];
    extern int sys_nerr;
#endif


    output = buffer;
    while((ch = *format++) != '\0')
    {
        switch(ch)
        {
            case '%':
                ljust = len = zpad = maxwidth = 0;
                longflag = pointflag = 0;
                nextch:
                ch = *format++;
                switch(ch)
                {
                    case 0:
                        dostr( "**end of format**" , 0);
                        return;
                    case '-': ljust = 1; goto nextch;
                    case '0': /* set zero padding if len not set */
                        if(len==0 && !pointflag) zpad = '0';
                    case '1':
                    case '2':
                    case '3':
                    case '4':
                    case '5':
                    case '6':
                    case '7':
                    case '8':
                    case '9':
                        if(pointflag)
                            maxwidth = maxwidth*10 + ch - '0';
                        else
                            len = len*10 + ch - '0';
                        goto nextch;
                    case '*': 
                        if(pointflag)
                            maxwidth = va_arg( args, int );
                        else
                            len = va_arg( args, int );
                        goto nextch;
                    case '.': pointflag = 1; goto nextch;
                    case 'l': longflag = 1; goto nextch;
                    case 'u':
                    case 'U':
                        /*fmtnum(value,base,dosign,ljust,len,zpad) */
                        if(longflag)
                        {
                            value = va_arg( args, long );
                        }
                        else
                        {
                            value = va_arg( args, int );
                        }
                        fmtnum( value, 10,0, ljust, len, zpad ); break;
                    case 'o':
                    case 'O':
                        /*fmtnum(value,base,dosign,ljust,len,zpad) */
                        if(longflag)
                        {
                            value = va_arg( args, long );
                        }
                        else
                        {
                            value = va_arg( args, int );
                        }
                        fmtnum( value, 8,0, ljust, len, zpad ); break;
                    case 'd':
                    case 'D':
                        if(longflag)
                        {
                            value = va_arg( args, long );
                        }
                        else
                        {
                            value = va_arg( args, int );
                        }
                        fmtnum( value, 10,1, ljust, len, zpad ); break;
                    case 'x':
                        if(longflag)
                        {
                            value = va_arg( args, long );
                        }
                        else
                        {
                            value = va_arg( args, int );
                        }
                        fmtnum( value, 16,0, ljust, len, zpad ); break;
                    case 'X':
                        if(longflag)
                        {
                            value = va_arg( args, long );
                        }
                        else
                        {
                            value = va_arg( args, int );
                        }
                        fmtnum( value,-16,0, ljust, len, zpad ); break;
                    case 's':
                        strvalue = va_arg( args, char *);
                        if(maxwidth > 0 || !pointflag)
                        {
                            if(pointflag && len > maxwidth)
                                len = maxwidth; /* Adjust padding */
                            fmtstr( strvalue,ljust,len,zpad, maxwidth);
                        }
                        break;
                    case 'c':
                        ch = va_arg( args, int );
                        dopr_outch( ch ); break;
                    case 'm':
#if HAVE_STRERROR 
                        dostr(strerror(SyslogErrno), 0);
#else
                        if(SyslogErrno < 0 || SyslogErrno >= sys_nerr)
                        {
                            dostr("Error ", 0);
                            fmtnum(SyslogErrno, 10, 0, 0, 0, 0);
                        }
                        else
                            dostr((char *)sys_errlist[SyslogErrno], 0);
#endif
                        break;

                    case '%': dopr_outch( ch ); continue;
                    default:
                        dostr(  "???????" , 0);
                }
                break;
            default:
                dopr_outch( ch );
                break;
        }
    }
    *output = 0;
}

void
fmtstr(  value, ljust, len, zpad, maxwidth )
char *value;
int ljust, len, zpad, maxwidth;
{
    int padlen, strlen;     /* amount to pad */

    if(value == 0)
    {
        value = "<NULL>";
    }
    for(strlen = 0; value[strlen]; ++ strlen); /* strlen */
    if(strlen > maxwidth && maxwidth)
        strlen = maxwidth;
    padlen = len - strlen;
    if(padlen < 0) padlen = 0;
    if(ljust) padlen = -padlen;
    while(padlen > 0)
    {
        dopr_outch( ' ' );
        --padlen;
    }
    dostr( value, maxwidth );
    while(padlen < 0)
    {
        dopr_outch( ' ' );
        ++padlen;
    }
}

void
fmtnum(  value, base, dosign, ljust, len, zpad )
long value;
int base, dosign, ljust, len, zpad;
{
    int signvalue = 0;
    unsigned long uvalue;
    char convert[20];
    int place = 0;
    int padlen = 0; /* amount to pad */
    int caps = 0;

    /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
        value, base, dosign, ljust, len, zpad )); */
    uvalue = value;
    if(dosign)
    {
        if(value < 0)
        {
            signvalue = '-';
            uvalue = -value;
        }
    }
    if(base < 0)
    {
        caps = 1;
        base = -base;
    }
    do
    {
        convert[place++] =
        (caps? "0123456789ABCDEF":"0123456789abcdef")
        [uvalue % (unsigned)base  ];
        uvalue = (uvalue / (unsigned)base );
    }while(uvalue);
    convert[place] = 0;
    padlen = len - place;
    if(padlen < 0) padlen = 0;
    if(ljust) padlen = -padlen;
    /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
        convert,place,signvalue,padlen)); */
    if(zpad && padlen > 0)
    {
        if(signvalue)
        {
            dopr_outch( signvalue );
            --padlen;
            signvalue = 0;
        }
        while(padlen > 0)
        {
            dopr_outch( zpad );
            --padlen;
        }
    }
    while(padlen > 0)
    {
        dopr_outch( ' ' );
        --padlen;
    }
    if(signvalue) dopr_outch( signvalue );
    while(place > 0) dopr_outch( convert[--place] );
    while(padlen < 0)
    {
        dopr_outch( ' ' );
        ++padlen;
    }
}

void
dostr( str , cut)
char *str;
int cut;
{
    if(cut)
    {
        while(*str && cut-- > 0) dopr_outch(*str++);
    }
    else
    {
        while(*str) dopr_outch(*str++);
    }
}

void
dopr_outch( c )
int c;
{
#if 0
    if(iscntrl(c) && c != '\n' && c != '\t')
    {
        c = '@' + (c & 0x1F);
        if(DoprEnd == 0 || output < DoprEnd)
            *output++ = '^';
    }
#endif
    if(DoprEnd == 0 || output < DoprEnd)
        *output++ = c;
    else
        SnprfOverflow++;
}

/*
**  QUAD_TO_STRING -- Convert a quad type to a string.
**
**	Convert a quad type to a string.  This must be done
**	separately as %lld/%qd are not supported by snprint()
**	and adding support would slow down systems which only
**	emulate the data type.
**
**	Parameters:
**		value -- number to convert to a string.
**
**	Returns:
**		pointer to a string.
*/

char *
quad_to_string(value)
QUAD_T value;
{
    char *fmtstr;
    static char buf[64];

    /*
    **  Use sprintf() instead of snprintf() since snprintf()
    **  does not support %qu or %llu.  The buffer is large enough
    **  to hold the string so there is no danger of buffer
    **  overflow.
    */

#if NEED_PERCENTQ
    fmtstr = "%qu";
#elif defined(ULONGIS64BIT)
    fmtstr = "%lu";
#else
    fmtstr = "%llu";
#endif
    sprintf(buf, fmtstr, value);
    return buf;
}
/*
**  SHORTENSTRING -- return short version of a string
**
**	If the string is already short, just return it.  If it is too
**	long, return the head and tail of the string.
**
**	Parameters:
**		s -- the string to shorten.
**		m -- the max length of the string.
**
**	Returns:
**		Either s or a short version of s.
*/

char *
shortenstring(s, m)
register const char *s;
int m;
{
    int l;
    static char buf[MAXSHORTSTR + 1];

    l = strlen(s);
    if(l < m)
        return(char *) s;
    if(m > MAXSHORTSTR)
        m = MAXSHORTSTR;
    else if(m < 10)
    {
        if(m < 5)
        {
            strncpy(buf, s, m);
            buf[m] = '\0';
            return buf;
        }
        strncpy(buf, s, m - 3);
        strcpy(buf + m - 3, "...");
        return buf;
    }
    m = (m - 3) / 2;
    strncpy(buf, s, m);
    strcpy(buf + m, "...");
    strcpy(buf + m + 3, s + l - m);
    return buf;
}

#endif
